feat: Generate SSO URL in Chatwoot, move Captain to primary tab (#9871)
- Generate SSO URL in Chatwoot, move Captain to the primary tab Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
@@ -32,6 +32,10 @@ class IntegrationsAPI extends ApiClient {
|
||||
deleteHook(hookId) {
|
||||
return axios.delete(`${this.baseUrl()}/integrations/hooks/${hookId}`);
|
||||
}
|
||||
|
||||
fetchCaptainURL() {
|
||||
return axios.get(`${this.baseUrl()}/integrations/captain/sso_url`);
|
||||
}
|
||||
}
|
||||
|
||||
export default new IntegrationsAPI();
|
||||
|
||||
@@ -17,6 +17,14 @@ const primaryMenuItems = accountId => [
|
||||
toState: frontendURL(`accounts/${accountId}/dashboard`),
|
||||
toStateName: 'home',
|
||||
},
|
||||
{
|
||||
icon: 'captain',
|
||||
key: 'captain',
|
||||
label: 'CAPTAIN',
|
||||
featureFlag: FEATURE_FLAGS.CAPTAIN,
|
||||
toState: frontendURL(`accounts/${accountId}/captain`),
|
||||
toStateName: 'captain',
|
||||
},
|
||||
{
|
||||
icon: 'book-contacts',
|
||||
key: 'contacts',
|
||||
|
||||
@@ -31,4 +31,5 @@ export const FEATURE_FLAGS = {
|
||||
INBOUND_EMAILS: 'inbound_emails',
|
||||
IP_LOOKUP: 'ip_lookup',
|
||||
LINEAR: 'linear_integration',
|
||||
CAPTAIN: 'captain_integration',
|
||||
};
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
"DESCRIPTION": "Chatwoot integrates with multiple tools and services to improve your team's efficiency. Explore the list below to configure your favorite apps.",
|
||||
"LEARN_MORE": "Learn more about integrations",
|
||||
"LOADING": "Fetching integrations",
|
||||
"CAPTAIN": {
|
||||
"DISABLED": "Captain is not enabled on your account.",
|
||||
"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."
|
||||
},
|
||||
"WEBHOOK": {
|
||||
"SUBSCRIBED_EVENTS": "Subscribed Events",
|
||||
"FORM": {
|
||||
|
||||
@@ -145,7 +145,11 @@
|
||||
},
|
||||
"AVAILABILITY": {
|
||||
"LABEL": "Availability",
|
||||
"STATUSES_LIST": ["Online", "Busy", "Offline"],
|
||||
"STATUSES_LIST": [
|
||||
"Online",
|
||||
"Busy",
|
||||
"Offline"
|
||||
],
|
||||
"SET_AVAILABILITY_SUCCESS": "Availability has been set successfully",
|
||||
"SET_AVAILABILITY_ERROR": "Couldn't set availability, please try again"
|
||||
},
|
||||
@@ -235,6 +239,7 @@
|
||||
"REPORTS": "Reports",
|
||||
"SETTINGS": "Settings",
|
||||
"CONTACTS": "Contacts",
|
||||
"CAPTAIN": "Captain",
|
||||
"HOME": "Home",
|
||||
"AGENTS": "Agents",
|
||||
"AGENT_BOTS": "Bots",
|
||||
|
||||
75
app/javascript/dashboard/routes/dashboard/Captain.vue
Normal file
75
app/javascript/dashboard/routes/dashboard/Captain.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useStoreGetters } from 'dashboard/composables/store';
|
||||
|
||||
import integrations from '../../api/integrations';
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
|
||||
const isLoading = ref(true);
|
||||
const captainURL = ref('');
|
||||
const hasError = ref(false);
|
||||
|
||||
const loadCaptainFrame = async integration => {
|
||||
if (!integration || !integration.enabled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isLoading.value = true;
|
||||
const { data } = await integrations.fetchCaptainURL();
|
||||
captainURL.value = data.sso_url;
|
||||
} catch (error) {
|
||||
hasError.value = true;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getters = useStoreGetters();
|
||||
const captainIntegration = computed(() =>
|
||||
getters['integrations/getIntegration'].value('captain', null)
|
||||
);
|
||||
|
||||
onMounted(() => loadCaptainFrame(captainIntegration.value));
|
||||
|
||||
watch(captainIntegration, updatedIntegration =>
|
||||
loadCaptainFrame(updatedIntegration)
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex-1 overflow-auto flex gap-8 flex-col font-inter text-slate-900 dark:text-slate-500"
|
||||
>
|
||||
<div class="flex-1 flex items-center justify-center">
|
||||
<div v-if="!captainIntegration">
|
||||
{{ $t('INTEGRATION_SETTINGS.CAPTAIN.DISABLED') }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="!captainIntegration.enabled"
|
||||
class="flex-1 flex flex-col gap-2 items-center justify-center"
|
||||
>
|
||||
<div>{{ $t('INTEGRATION_SETTINGS.CAPTAIN.DISABLED') }}</div>
|
||||
<router-link :to="{ name: 'settings_applications' }">
|
||||
<woot-button class="clear link">
|
||||
{{ $t('INTEGRATION_SETTINGS.CAPTAIN.CLICK_HERE_TO_CONFIGURE') }}
|
||||
</woot-button>
|
||||
</router-link>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="isLoading"
|
||||
class="flex-1 flex items-center justify-center"
|
||||
>
|
||||
<Spinner color-scheme="primary" />
|
||||
<span>{{ $t('INTEGRATION_SETTINGS.CAPTAIN.LOADING_CONSOLE') }}</span>
|
||||
</div>
|
||||
<div v-else-if="!isLoading && hasError">
|
||||
{{ $t('INTEGRATION_SETTINGS.CAPTAIN.FAILED_TO_LOAD_CONSOLE') }}
|
||||
</div>
|
||||
<iframe
|
||||
v-else-if="!isLoading && captainURL"
|
||||
:src="captainURL"
|
||||
class="w-full min-h-[800px] h-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -8,6 +8,7 @@ import { frontendURL } from '../../helper/URLHelper';
|
||||
import helpcenterRoutes from './helpcenter/helpcenter.routes';
|
||||
|
||||
const AppContainer = () => import('./Dashboard.vue');
|
||||
const Captain = () => import('./Captain.vue');
|
||||
const Suspended = () => import('./suspended/Index.vue');
|
||||
|
||||
export default {
|
||||
@@ -17,6 +18,14 @@ export default {
|
||||
path: frontendURL('accounts/:account_id'),
|
||||
component: AppContainer,
|
||||
children: [
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/captain'),
|
||||
name: 'captain',
|
||||
component: Captain,
|
||||
meta: {
|
||||
permissions: ['administrator', 'agent'],
|
||||
},
|
||||
},
|
||||
...inboxRoutes,
|
||||
...conversation.routes,
|
||||
...settings.routes,
|
||||
|
||||
@@ -24,12 +24,14 @@ export const getters = {
|
||||
getAppIntegrations($state) {
|
||||
return $state.records;
|
||||
},
|
||||
getIntegration: $state => integrationId => {
|
||||
const [integration] = $state.records.filter(
|
||||
record => record.id === integrationId
|
||||
);
|
||||
return integration || {};
|
||||
},
|
||||
getIntegration:
|
||||
$state =>
|
||||
(integrationId, defaultValue = {}) => {
|
||||
const [integration] = $state.records.filter(
|
||||
record => record.id === integrationId
|
||||
);
|
||||
return integration || defaultValue;
|
||||
},
|
||||
getUIFlags($state) {
|
||||
return $state.uiFlags;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user